﻿using System;
using System.Collections;
using System.IO;

namespace Social_Network_Analyzer
{
    class Analyzer
    {
        private TextReader tr;
        private int numV;
        private int numE;
        private bool[,] adjacencyMatrix;
        private ArrayList cliqueList = new ArrayList();
        private Person[] personList;

        /// <summary>
        /// The class that analyzes a text document as a social network.  Displays different statistics on end of run.
        /// </summary>
        /// <param name="path"></param>
        public Analyzer(string path)
        {
            tr = new StreamReader(path);

            int minVertex = ParseFile();
            FindCliques(minVertex);
            PrintMatrix();
            PrintStats();

            tr.Close();
            Console.Read();
        }

        /// <summary>
        /// Parses the input file.
        /// </summary>
        /// <returns>the minimum vertex.</returns>
        private int ParseFile()
        {
            //Get number of Vertices
            numV = Int32.Parse(tr.ReadLine());
            personList = new Person[numV];

            //Get Number of Edges
            numE = Int32.Parse(tr.ReadLine());

            //Initialize the vertices
            int minV = -1;
            for (int i = 1; i <= numV; i++)
            {
                string temp = tr.ReadLine();
                string[] tempSplit = temp.Split(' ');
                int vertexId = Int32.Parse(tempSplit[0]);
                if (minV == -1)
                    minV = vertexId;
                string name = tempSplit[1];

                personList[vertexId - minV] = new Person(vertexId, name);
            }
            adjacencyMatrix = new bool[numV, numV];
            //Initialize the edges
            for (int i = 1; i <= numE; i++)
            {
                string temp = tr.ReadLine();
                string[] tempSplit = temp.Split(' ');
                int u = Int32.Parse(tempSplit[0]);
                int v = Int32.Parse(tempSplit[1]);
                if (u == v)
                    throw new Exception();
                else
                {
                    adjacencyMatrix[u - minV, v - minV] = true;
                    adjacencyMatrix[v - minV, u - minV] = true;
                }
            }
            return minV;
        }

        /// <summary>
        /// Finds all of the cliques.
        /// </summary>
        /// <param name="minVertex"></param>
        private void FindCliques(int minVertex)
        {
            // This gets all the groups of 3 person cliques.
            for (int i = 0; i < personList.Length - 2; i++)
            {
                for (int j = i + 1; j < personList.Length - 1; j++)
                {
                    if (adjacencyMatrix[i,j])
                        for (int k = j + 1; k < personList.Length; k++)
                        {
                            if (adjacencyMatrix[i, k] && adjacencyMatrix[j, k])
                            {
                                Clique clique = new Clique();
                                clique.addPerson(personList[i]);
                                clique.addPerson(personList[j]);
                                clique.addPerson(personList[k]);
                                cliqueList.Add(clique);
                            }
                        }
                }
            }

            // We now see if any of these small cliques can be merged together.
            bool newLoop = false;
            do
            {
                newLoop = false;
                for (int x = 0; x < cliqueList.Count; x++)
                {
                    Clique c1 = cliqueList[x] as Clique;
                    if (newLoop)
                        break;
                    for (int y = x + 1; y < cliqueList.Count; y++)
                    {
                        if (newLoop)
                            break;
                        Clique c2 = cliqueList[y] as Clique;
                        if (!c1.Equals(c2))
                        {
                            bool canMerge = true;
                            for (int i = 0; i < c1.getCliqueSize(); i++)
                            {
                                for (int j = 0; j < c2.getCliqueSize(); j++)
                                {
                                    Person c1Temp = c1.getMember(i);
                                    Person c2Temp = c2.getMember(j);
                                    if (!c1Temp.Equals(c2Temp))
                                    {
                                        if (!adjacencyMatrix[c1Temp.vertexId - minVertex, c2Temp.vertexId - minVertex])
                                            canMerge = false;
                                    }
                                }
                            }
                            if (canMerge)
                            {
                                newLoop = true;
                                for (int j = 0; j < c2.getCliqueSize(); j++)
                                {
                                    c1.addPerson(c2.getMember(j));
                                }
                                cliqueList.Remove(c2);
                            }
                        }
                    }
                }
            }
            while (newLoop);

        }

        /// <summary>
        /// Outputs the adjacency matrix for viewing.
        /// </summary>
        private void PrintMatrix()
        {
            for (int i = 0; i < numV; i++)
            {
                for (int j = 0; j < numV; j++)
                {
                    if (i == j)
                        Console.Write("X ");
                    else
                        Console.Write(adjacencyMatrix[i, j] ? "1 " : "0 ");
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }

        /// <summary>
        /// Prints out all required statistics.
        /// </summary>
        private void PrintStats()
        {
            int min = -1, max = -1, sum;
            float avg = 0;
            for (int i = 0; i < numV; i++)
            {
                sum = 0;
                for (int j = 0; j < numV; j++)
                {
                    if (adjacencyMatrix[i, j])
                        sum++;
                }
                if (min == -1 || sum < min)
                    min = sum;
                if (max == -1 || sum > max)
                    max = sum;
                avg += sum;
            }
            avg = avg / numV;
            Console.WriteLine("Vertices: " + numV);
            Console.WriteLine("Edges: " + numE);
            Console.WriteLine("Minimum Degree: " + min);
            Console.WriteLine("Maximum Degree: " + max);
            Console.WriteLine("Average Degree: " + avg);
            if (cliqueList.Count == 0)
            {
                Console.WriteLine("No cliques detected!");
                return;
            }
            Console.WriteLine("Number of cliques: " + cliqueList.Count);
            max = -1;
            foreach (Clique c in cliqueList)
            {
                if (max == -1 || c.getCliqueSize() > max)
                    max = c.getCliqueSize();
            }
            Console.WriteLine("Maximum clique size: " + max);
            Console.WriteLine("Maximum clique(s) members:");
            foreach (Clique c in cliqueList)
            {
                if (c.getCliqueSize() == max)
                    c.outputMembers();
            }
        }
    }
}
